import {AjaxDataType, AjaxType, YozoApiMethod, YozoApiURI, YozoMethod} from "./constant/WebOfficeCons";
import {Dictionary, MessageData, ProxyOpts, YozoAjaxOpts} from "./types/Types";
import {getFnName, invokeFunction} from "./function/CommonUtils";
import {CallBackFunCache} from "./classes/CallBackFunCache";
import {AbstractWebOffice} from "./AbstractWebOffice";

export class YozoWebOffice extends AbstractWebOffice{
    private static _pluginType:string='yozo';
    private corsTargetWindow: Window|null;
    private readonly contentElement:HTMLIFrameElement;
    _ready:boolean=false;
    private callbackCache:CallBackFunCache=new CallBackFunCache();
    private readonly targetServerUrl:string;
    constructor(wrapperId:string,options:Dictionary) {
        super(wrapperId,options);
        const wrapperEle=document.getElementById(wrapperId);
        if (wrapperEle == null)
            throw new Error("id为" + wrapperId + "的元素不存在!");
        const htmlObj:HTMLCollectionOf<HTMLElement> = wrapperEle.getElementsByTagName("iframe");
        if(htmlObj.length>0){
            for(let i=0;i<htmlObj.length;i++){
                const item:HTMLElement|null=htmlObj.item(i);
                if (item && item.parentNode)
                    item.parentNode.removeChild(item);
            }
        }
        window.addEventListener("message", event=>{
            this.messageHandler(event)
        }, false);
        this.contentElement = document.createElement("iframe");
        this.contentElement.id=this.options.tagId;
        this.contentElement.width='100%';
        this.contentElement.height='100%';
        this.contentElement.allowFullscreen=true;
        this.contentElement.frameBorder='0';
        (<HTMLElement>wrapperEle).appendChild(this.contentElement);
        this.contentElement.src = this.options.fileDownloadUrl;
        this.corsTargetWindow = this.contentElement.contentWindow;
        if(this.corsTargetWindow!=null)
            this.targetServerUrl=this.corsTargetWindow.location.protocol+"//"+this.corsTargetWindow.location.host;
    }

    protected _createObjectHtml(objId:string):string{
        return "";
    }

    private messageHandler(event: any) {
        if (event.origin !== this.targetServerUrl)
            return;
        let data: MessageData = event.data;
        if(data.ready) {
            this._ready = true;
            return;
        }
        invokeFunction(data.completeCb,this.callbackCache,data.result);
        this.callbackCache.removeFunc(data.completeCb);
        // window[data.completeCb](data.result);
    }

    public show(){
        this.contentElement.style.display='';
    }

    public hide(){
        this.contentElement.style.display='none';
    }

    private proxy(method: YozoMethod, args?: any): Promise<any> {
        const that = this;
        return new Promise(function (resolve, reject) {
            let proxyOpts: ProxyOpts;
            proxyOpts = {method: method, args: args};
            proxyOpts.success = this.callbackCache.cacheAndConvert(resolve);
            proxyOpts.fail = this.callbackCache.cacheAndConvert(reject);
            that.doProxy(proxyOpts);
        });
    }

    private doProxy(options: ProxyOpts) {
        if(this._ready) {
            (<Window>this.corsTargetWindow).postMessage(options, this.targetServerUrl);
            return;
        }
        let startTime:number=new Date().getTime();
        let intervalC=setInterval(()=>{
            if (!this._ready){
                if(new Date().getTime()-startTime>5000)
                    throw new Error("proxy is not ready!");
                return;
            }
            (<Window>this.corsTargetWindow).postMessage(options, this.targetServerUrl);
            clearInterval(intervalC);
        },200);

    }

    defaultOpenFileOpts={
        saveFlag:true,
        readOnly:false,
        mobileFlag:false
    };


    private _execCommand(command:string|Function):Promise<any> {
        let _command=command;
        if(_command instanceof Function) {
            let commandStr = getFnName(_command);
            if(commandStr.length<_command.toString().length)
                _command=_command.toString();
            else
                _command=commandStr;
        }
        return this.proxy(YozoMethod.EXECCOMMAND, _command);
    }
    private buildUrl(URI: string): string {
        return this.targetServerUrl + (this.targetServerUrl.endsWith("/") ? URI : "/" + URI);
    }

    private buildQueryString(method: number, params?: Dictionary<any>): string {
        let data: Dictionary<any> = {'method': method};
        if(params!==undefined)
            data['params']=params;
        return "jsonParams=" + encodeURIComponent(JSON.stringify(data));
    }

    private buildUrlWithQueryString(URI: string, method: number, params?: Dictionary<any>): string {
        return this.buildUrl(URI) + "?" + this.buildQueryString(method, params);
    }


    public openRemoteFile(url: string, readOnly: boolean):Promise<any> {
        return Promise.resolve(undefined);
    }


    public saveToUrl(): Promise<any> {
        return this.proxy(YozoMethod.EXECCOMMAND, 'YozoOffice.save()');
    }


    public createFile(fileType='1',fileName?:string,filePath?:string): Promise<any> {
        if (fileType ===undefined)
            throw new Error("fileType不能为空！");
        let params: Dictionary<any> = {"fileType": fileType};
        if (fileName != undefined)
            params["fileName"]=fileName;
        if (filePath !==undefined)
            params["filePath"]= filePath;
        let url: string = this.buildUrlWithQueryString(YozoApiURI.FILEMANAGEMENT_URI, YozoApiMethod.CREATEDOC, params);
        let ajaxOpts: YozoAjaxOpts = {url: url, type: AjaxType.GET, dataType: AjaxDataType.json};
        return this.proxy(YozoMethod.REQUESTSERVER, ajaxOpts);
    }

    public saveAs(filePath: string, isDeleteFile?: boolean): void {
        if (filePath == null) {
            throw new Error("filePath不能为空!");
        }
        let form = document.createElement("form");
        form.action = this.buildUrl(YozoApiURI.DOWNLOAD_URI);
        form.method = 'POST';
        form.acceptCharset = 'UTF-8';
        form.target = 'rfFrameForDownloadFile';
        let filePathInput = document.createElement("input");
        filePathInput.type = 'hidden';
        filePathInput.name = 'filePath';
        filePathInput.value = filePath;
        form.appendChild(filePathInput);
        if (isDeleteFile !== undefined) {
            let isDeleteFileInput = document.createElement("input");
            isDeleteFileInput.type = 'hidden';
            isDeleteFileInput.name = 'isDeleteFile';
            isDeleteFileInput.value = '' + isDeleteFile;
            form.appendChild(isDeleteFileInput);
        }
        //利用iframe防止页面因为跳转而关闭
        let rfFrameForDownloadFile = document.createElement("iframe");
        rfFrameForDownloadFile.id = 'rfFrameForDownloadFile';
        rfFrameForDownloadFile.name = 'rfFrameForDownloadFile';
        rfFrameForDownloadFile.src = 'about:blank';
        rfFrameForDownloadFile.style.display = 'none';
        document.body.appendChild(rfFrameForDownloadFile);
        document.body.appendChild(form);
        form.submit();
        document.removeChild(form);
        document.removeChild(rfFrameForDownloadFile);
    }

    public isBookmarkExists(bookmarkName:string):boolean {
        const result:Dictionary =this.proxy(YozoMethod.ADDBOOKMARK,undefined);
        return result.result;
    }


    public addBookmark(name:string):any{
        const result:Dictionary =this.proxy(YozoMethod.ADDBOOKMARK,name);
        return result.result;
    }


    public deleteBookmark(name:string):void{
        const result:Dictionary =this.proxy(YozoMethod.DELETEBOOKMARK,name);
        return result.result;
    }


    public getBookmarkValue(name:string):string|undefined{
        const result:Dictionary = this.proxy(YozoMethod.GETBOOKMARKVALUE,name);
        return result.result;
    }


    public getAllBookmarkValue():Object{
        const result:Dictionary =this.proxy(YozoMethod.GETALLBOOKMARKVALUE);
        return result.result;
    }


    public setBookmarkValue(name:string,value:string):void{
        const result:Dictionary = this.proxy(YozoMethod.SETBOOKMARKVALUE, [name,value]);
        return result.result;
    }


    public locateBookmark(name:string):void{
        const result:Dictionary =  this.proxy(YozoMethod.LOCATEBOOKMARK,name);
        return result.result;
    }


    public acceptAllRevisions():void{
        const result:Dictionary = this._execCommand("YozoOffice.Application.ActiveDocument.AcceptAllRevisions();");
        return result.result;
    }


    public rejectAllRevisions():void{
        this._execCommand("YozoOffice.Application.ActiveDocument.RejectAllRevisions();");
    }


    public trackRevisions(enable=true):void{
        this._execCommand("YozoOffice.Application.ActiveDocument.TrackRevisions="+enable+";");
    }


    public showRevisions(show:boolean=true):void{
        this._execCommand("YozoOffice.Application.ActiveWindow.View.RevisionsFilter.Markup="+(show?'2':'0'));
    }


    public print():void{
        this._execCommand("document.querySelector(\"#File_Printer\").click()");
    }

    importFile(): Promise<any> {
        return Promise.resolve(undefined);
    }

    openLocalFile(filePath: string,readOnly:boolean): Promise<any> {
        return Promise.resolve(undefined);
    }


    enableToolbars(enable: boolean=true): void {
    }

    getCellText(tableIndex: number, rowIndex: number, cellIndex: number): string|undefined {
        const result:Dictionary=this.proxy(YozoMethod.GETCELLTEXT);
        return result.result;
    }

    setCellText(tableIndex: number, rowIndex: number, cellIndex: number, text: string): void {

    }

    runMacro(method:string,command?:string):any{
        throw new Error("该控件不支持执行宏命令");
    }


    insertDocumentFromUrl(documentUrl: string): Promise<boolean> {
        throw new Error("该控件不支持该方法");
    }

    insertImage(imageUrl: string, pageNo: number = 0, left: number = -999999, top: number = -999999, width: number = 0, height: number = 0) {
        throw new Error("该控件不支持该方法");
    }

    addWordImageWaterMark(imagePath: string, left: number = -999995, top: number = -999995, width: number = 0, height: number = 0, rotation: number = 0) {
        throw new Error("该控件不支持该方法");
    }

    addWordTextWaterMark(text: string, left: number = -999995, top: number = -999995, fontFamily: string = '宋体', fontSize: number = 30, color: number = 0xd0d0d0, width: number = 0, height: number = 0, rotation: number = 0, percent: number = 0) {
        throw new Error("该控件不支持该方法");
    }

    closePrintPreview(): void {
    }

    printPreview(): void {
    }
}